<template>
  <!-- v5.1.0，新增加载效果，避免页面闪现 -->
  <div v-if="enabledLoading && visibleLoading" class="loading-wrap" :class="{ hidden: !loading }">
    <AppLoading/>
  </div>
  <template v-else>
    <router-view/>
    <LoginFormWindow/>
  </template>
</template>

<script>
import { useDefaultStore} from '@/core/store'
import { mapState, mapActions } from 'pinia'
import pkg from '../package.json'
import constants from '@/core/plugins/consts'
import router from '@/router'
import LoginFormWindow from '@/components/system/LoginFormWindow'
import AppLoading from '@/components/common/AppLoading'
import { fetchUserMenus } from '@/api/system/index'

export default {
  components: { AppLoading, LoginFormWindow},
  data () {
    return {
      // v5.1.0，是否启用加载动画
      enabledLoading: import.meta.env.VITE_APP_LOADING_ANIMATION === 'on',
      // v5.1.0，是否展示loading
      visibleLoading: true,
      loading: true
    }
  },
  computed: {
    ...mapState(useDefaultStore, ['userInfo', 'menuData', 'homePage'])
  },
  watch: {
    // 用户信息发生变化时，重新初始化路由
    async userInfo () {
      if (this.userInfo == null) {
        return
      }
      await this.initRoutes()
    }
  },
  methods: {
    ...mapActions(useDefaultStore, ['switchCollapseMenu']),
    /**
     * 为静态引入添加版本号
     *
     * @param tag 标签
     * @param prop url属性名称
     * @param dataProp 预置的url属性名称
     */
    handleVersion (tag, prop, dataProp) {
      const tags = document.querySelectorAll(tag)
      for (const tag of tags) {
        const uri = tag.getAttribute(dataProp)
        if (uri == null || uri === '') {
          continue
        }
        const prefix = import.meta.env.BASE_URL === '/' ? '' : import.meta.env.BASE_URL
        tag[prop] = `${prefix}${uri}?v=${pkg.version}`
      }
    },
    /**
     * 初始化本地配置
     */
    initLocalConfig () {
      // 菜单状态配置
      const menuStatus = window.localStorage.getItem('MENU_STATUS')
      if (menuStatus != null) {
        this.switchCollapseMenu(menuStatus === 'true')
      }
    },
    /**
     * 初始化路由
     *
     * @returns {Promise<void>}
     */
    async initRoutes () {
      try {
        // 重置菜单
        this.$defaultStore.menuData.list = []
        // 获取菜单
        const storeMenus = this.menuData.list
        const menus = await fetchUserMenus()
        // 没有菜单，跳转到无权访问提示页
        if (menus.length === 0) {
          this.$router.push({
            name: 'error',
            params: {
              type: 'not-allowed'
            }
          })
          return
        }
        // 设置首页
        const homePage = this.__getHomePage(menus)
        // - 没有找到合适的首页（非外部链接、IFrame嵌入的菜单），则跳转到无权访问提示页
        if (homePage == null) {
          this.$router.push({
            name: 'error',
            params: {
              type: 'not-allowed'
            }
          })
          return
        }
        this.$defaultStore.homePage = this.__getHomePage(menus)
        // 添加菜单
        storeMenus.push.apply(storeMenus, menus)
        // 添加路由
        const viewsComponents = import.meta.glob('@/views/**/**.vue')
        this.__addRouters(storeMenus, [], viewsComponents)
        // 404捕获，需要在路由添加完成后，将其添加到最后，避免刷新时路由还未加载直接出现404
        router.addRoute({
          path: '/:catchAll(.*)',
          redirect: '/error/not-found'
        })
        // 首页
        router.addRoute({
          name: 'index',
          path: '/',
          redirect: this.homePage.uri
        })
        // v5.1.0，如果存在上下文路径，则补充上下文路径路由，避免登录后找不到路由
        const contextPath = import.meta.env.VITE_APP_CONTEXT_PATH
        if (contextPath !== '/' && contextPath !== './') {
          router.addRoute({
            path: contextPath,
            redirect: this.homePage.uri
          })
        }
        // v5.1.0，等待路由准备好后重新进行页面跳转，使路由生效
        router.isReady()
          .then(() => {
            // 直接访问域名，跳转至首页
            if (this.$route.path === '/') {
              this.$router.push(this.homePage.uri)
            }
            // 访问其它页，重新加入路由
            else {
              // v5.1.0，修复刷新后未能保留参数的BUG
              this.$router.push(this.$route)
            }
          })
          .catch(e => {
            throw e
          })
      } catch (e) {
        console.error('初始化路由失败', e)
        throw e
      }
    },
    /**
     * 新建路由
     *
     * @param routes 需添加的路由
     * @param parents 需添加到的目标列表
     * @param viewComponents 页面组件
     * @private
     */
    __addRouters (routes, parents = [], viewComponents) {
      if (routes == null || routes.length === 0) {
        return
      }
      const rs = router.getRoutes()
      for (const route of routes) {
        const parentsDump = JSON.parse(JSON.stringify(parents))
        parentsDump.push(route)
        if (route.type === 'DIR') {
          this.__addRouters(route.children, parentsDump, viewComponents)
          continue
        }
        // 外链和嵌入链，不添加路由
        if (route.type === 'EXTERNAL' || route.type === 'IFRAME') {
          continue
        }
        if (rs.findIndex(r => r.path === route.path) > -1) {
          this.__addRouters(route.children, parentsDump, viewComponents)
          continue
        }
        if (this.homePage == null) {
          this.defaultStore.homePage = route
        }
        // v5.1.0，支持菜单路径携带参数
        let path = route.uri
        if (path != null && path.indexOf('?') !== -1) {
          path = path.substring(0, path.indexOf('?'))
        }
        const viewComponent = viewComponents[`/src/views${path}.vue`]
        if (viewComponent != null) {
          router.addRoute('layout', {
            path,
            // v5.1.0，避免name重复
            name: route.index,
            meta: {
              title: route.name,
              paths: [...parents.map(p => p.name), route.name]
            },
            component: viewComponent
          })
        } else {
          // 标记为不存在
          route.__exists = false
          console.error(`未找到路由组件：@/views${route.uri}.vue`)
        }
        this.__addRouters(route.children, parentsDump, viewComponents)
      }
    },
    /**
     * 获取首页
     * @private
     */
    __getHomePage (menus) {
      for (const menu of menus) {
        // 目录，继续查找子菜单
        if (menu.type === constants.SYSTEM_MENU.TYPE_DIR) {
          const homePage = this.__getHomePage(menu.children)
          // 在当前目录下未找到合适的菜单作为首页，则继续查找下一级菜单
          if (homePage == null) {
            continue
          }
          return homePage
        }
        // 外部链接，不能作为首页
        if (menu.type === constants.SYSTEM_MENU.TYPE_EXTERNAL) {
          continue
        }
        // IFrame嵌套，不能作为首页
        if (menu.type === constants.SYSTEM_MENU.TYPE_IFRAME) {
          continue
        }
        return menu
      }
      return null
    }
  },
  async created () {
    this.handleVersion('link', 'href', 'data-href')
    this.handleVersion('script', 'src', 'data-src')
    // 已登录，则初始化路由
    if (this.userInfo != null) {
      await this.initRoutes()
    }
    // v5.1.0，初始化主题
    if (this.$theme.enabled) {
      await this.$theme.init()
    }
    // v5.1.0，隐藏loading动画
    // 此处不能使用nextTick，否则会导致关闭主题的情况下，背景没有过度效果
    setTimeout(() => {
      this.loading = false
      // 此处延迟800毫秒，等待背景过度完成后再关闭
      setTimeout(() => {
        this.visibleLoading = false
      }, 1200)
    }, 0)
  },
  mounted () {
    this.initLocalConfig()
  }
}
</script>
<style scoped lang="scss">
// v5.1.0，新增loading
$--delay-time: 1.2s;
.loading-wrap {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  opacity: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  // 隐藏
  &.hidden {
    &::before {
      filter: blur(0);
    }
  }
  // 背景层
  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: url("/public/images/login.jpg");
    background-repeat: no-repeat;
    background-size: auto 180%;
    background-clip: content-box;
    background-position: center;
    filter: blur(8px);
    transition: all $--delay-time;
  }
  // 覆盖颜色透明度
  &::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, .45);
  }
}
</style>
